# -*- coding:utf-8 -*-
"""
@Author : 审心
@Time : 2023/4/17 10:37y
"""

import redis
import json

import config
from common.utils import ObjUtil


def value_to_save_format(value):
    """
    将值转换为保存格式
    :param value:
    :return:
    """
    if value:
        if isinstance(value, (str, bytes, int, float)):
            return value
        elif isinstance(value, (list, dict, tuple)):
            return json.dumps(value, ensure_ascii=False)
        else:
            return json.dumps(ObjUtil.to_dict(value), ensure_ascii=False)
    else:
        return None


def base_value_convert(value, type_=None):
    if value is None:
        return None
    if type_ is None:
        return value
    if type_ == str:
        return value.decode('utf-8')
    if isinstance(type_, type) and issubclass(type_, (str, bytes, int, float)):
        return type_(value)
    if issubclass(type_, (list, tuple, set)):
        return json.loads(value.decode('utf-8'))
    if issubclass(type_, dict):
        return json.loads(value.decode('utf-8'))
    return None


def hs_set_value_convert(values, type_=None):
    if not values:
        return []
    if type_ is None:
        return [v for v in values]
    if issubclass(type_, (str, bytes, int, float)):
        return [type_(v) for v in values]
    if issubclass(type_, (list, tuple, set)):
        return [type_(json.loads(v.decode('utf-8'))) for v in values]
    if issubclass(type_, dict):
        return [json.loads(v.decode('utf-8')) for v in values]
    return []


class RedisUtil:
    def __init__(self, host, port, db, password=None):
        """
        初始化RedisUtil对象，并连接Redis数据库
        :param host: Redis服务器地址
        :param port: Redis服务器端口
        :param db: Redis数据库编号
        :param password: Redis服务器密码
        """
        self.r = redis.Redis(host=host, port=port, db=db, password=password)

    def put(self, key: str, value, ex=None):
        """
        设置Redis键值对
        :param key: 键名
        :param value: 键值，可以是基本数据类型或字典、列表等非基本数据类型
        :param ex: 过期时间（秒）
        """
        self.r.set(key, value_to_save_format(value), ex=ex)

    def h_set_put(self, key: str, field: str, value, ex=None):
        """
        对 HashSet 添加值
        :param key: redis 键名
        :param field: 字段名
        :param value: 值
        :param ex: 过期时间（秒）
        :return:
        """
        self.r.hset(key, field, value_to_save_format(value))
        if ex:
            self.r.expire(key, ex)

    def h_set_get(self, key: str, field: str = None, key_type=None, value_type=None):
        """
        获取 HashSet 值
        :param key: 键名
        :param field: 字段名
        :param key_type: 字段类型
        :param value_type: 值类型
        :return:
        """
        if field:
            return base_value_convert(self.r.hget(key, field), value_type)
        else:
            dict_ = self.r.hgetall(key)
            return [{base_value_convert(k, key_type): base_value_convert(v, value_type)} for k, v in dict_.items()]

    def get(self, key: str, type_=None):
        """
        获取Redis键值对的值
        :param key: 键名
        :param type_: 指定返回值的类型（str、int、float、list、tuple、set、dict），默认为原始类型
        :return: 返回Redis键值对的值，如果指定了type_，则返回对应类型的值
        """
        value = self.r.get(key)
        return base_value_convert(value, type_)

    def delete(self, key: str):
        """
        删除指定的键值对
        :param key: 键名
        """
        self.r.delete(key)

    def exists(self, key: str):
        """
        判断指定的键是否存在
        :param key: 键名
        :return: 如果键存在，返回True；否则返回False
        """
        return self.r.exists(key)

    def keys(self, pattern: str):
        """
        使用模糊匹配获取 Redis 的 keys

        Args:
            pattern: 匹配模式，支持通配符 * 和 ?

        Returns:
            匹配成功的 key 列表
        """
        keys = self.r.keys(pattern)
        return [key.decode('utf-8') for key in keys]

    def expire(self, name, time):
        """
        设置Redis的指定键的过期时间
        :param name: 键名
        :param time: 过期时间，单位为秒
        :return: 如果成功返回True，否则返回False
        """
        return self.r.expire(name, time)

    def ttl(self, name):
        """
        获取Redis的指定键的剩余过期时间
        :param name: 键名
        :return: 返回剩余过期时间，如果键不存在或不过期，则返回-1，如果发生错误，则返回-2
        """
        return self.r.ttl(name)

    def flushall(self):
        """
        删除所有Redis的键
        """
        self.r.flushall()
